package SGEngine2

import (
	//"fmt"
	"freetypego"
	"github.com/gl"
	"image"
	"image/draw"
)

var (
	FTCache FT
)

type FT struct {
	isInit bool
	Fonts  map[string]freetypego.FT_Face
}

type FTChar struct {
	CharCode int
	AdvanceX int
	BBox     *freetypego.FT_BBox
	TexRect  Rect
	Glyph    *freetypego.FT_GlyphSlot
}

type FTFont struct {
	MinSize  int
	FontSize int
	CharMap  map[rune]*FTChar

	FontMat   *Material
	faceIndex freetypego.FT_Face

	CurDrawPos Vec2
}

func (f *FT) Init() {
	if f.isInit == false {
		freetypego.InitFreeType()
		f.Fonts = make(map[string]freetypego.FT_Face)
	}

}

func NewFTFont(path string, fontsize int) *FTFont {
	FTCache.Init()
	font := &FTFont{
		MinSize: 512, FontSize: fontsize, CharMap: make(map[rune]*FTChar),
		FontMat: &Material{
			path,
			ShaderManager.SahderGet("Texture"),
			&Image{
				image.NewNRGBA(image.Rect(0, 0, 512, 512)),
				gl.GenTexture(),
			},
			true,
		}}
	if faceindex, ok := FTCache.Fonts[path]; ok {
		font.faceIndex = faceindex
	} else {
		FTCache.Fonts[path] = freetypego.FT_New_Face(path)
		font.faceIndex = FTCache.Fonts[path]
	}
	freetypego.Face_Set_Pixel_Sizes(int(font.faceIndex), fontsize, fontsize)
	return font
}

func (f *FTFont) GetChar(char rune) *FTChar {
	if v, ok := f.CharMap[char]; ok {
		return v
	}
	newchar := f.drawChar(char)
	f.CharMap[char] = newchar
	return newchar
}

func (f *FTFont) drawChar(char rune) *FTChar {
	newchar := &FTChar{CharCode: int(char)}
	freetypego.Face_Load_Char(int(f.faceIndex), newchar.CharCode, 0x4|0x8)
	f.faceIndex.SaveCurGlyphToCache()
	f.faceIndex.CacheBBox()
	newchar.BBox = freetypego.GetCacheBBox()
	GlyphSlot := f.faceIndex.GetGlyphSlot()
	newchar.Glyph = GlyphSlot
	newchar.AdvanceX = int(GlyphSlot.Advance.X) / int(64)
	CharImg := GlyphSlot.BitMap.ToNGRBA()

	if f.CurDrawPos.X+float64(GlyphSlot.BitMap.Width) >= float64(f.MinSize) {
		f.CurDrawPos.X = 0
		f.CurDrawPos.Y += float64(f.FontSize)
	}
	newchar.TexRect = Rect{f.CurDrawPos.X, f.CurDrawPos.Y,
		f.CurDrawPos.X + float64(GlyphSlot.BitMap.Width), f.CurDrawPos.Y + float64(GlyphSlot.BitMap.Rows)}
	f.CurDrawPos.X += float64(GlyphSlot.BitMap.Width)
	draw.Draw(f.FontMat.Tex.Texture, image.Rect(int(newchar.TexRect.MinX), int(newchar.TexRect.MinY),
		int(newchar.TexRect.MaxX), int(newchar.TexRect.MaxY)), CharImg, image.ZP, draw.Over)

	data := make([]byte, 512*512*4)
	lineLen := 512 * 4
	dest := len(data) - lineLen
	for src := 0; src < len(f.FontMat.Tex.Texture.Pix); src += f.FontMat.Tex.Texture.Stride {
		copy(data[dest:dest+lineLen], f.FontMat.Tex.Texture.Pix[src:src+f.FontMat.Tex.Texture.Stride])
		dest -= lineLen
	}
	f.FontMat.Tex.TexID.Bind(gl.TEXTURE_2D)
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
	gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
	gl.TexImage2D(gl.TEXTURE_2D, 0, 4, 512, 512, 0, gl.RGBA, gl.UNSIGNED_BYTE, data)
	return newchar
}

func (f *FTFont) GenFTLabelMesh(text string, pivotOffset Vec2, c4 Color4) (*Mesh, *Bounds) {
	strLen := len([]rune(text))
	var mesh = new(Mesh)
	mesh.MeshType = 1
	RtBounds := new(Bounds)
	RtBounds.Center = Vec3{0, 0, 0}
	mesh.Vertices = make([]float32, strLen*18)
	mesh.UV = make([]float32, strLen*12)
	mesh.Color = make([]float32, strLen*24)
	halfx := f.GetHalfX(text)
	RtBounds.Min.X = (0 - pivotOffset.X) * float64(halfx) * float64(2)
	RtBounds.Min.Y = (0 - pivotOffset.Y) * float64(f.FontSize)
	RtBounds.Max.X = (1 - pivotOffset.X) * float64(halfx) * float64(2)
	RtBounds.Max.Y = (1 - pivotOffset.Y) * float64(f.FontSize)

	f.FillFTLabelMesh(text, -pivotOffset.X*float64(halfx)*float64(2), -pivotOffset.Y*float64(f.FontSize), mesh, c4, RtBounds)

	mesh.Vertbuffer = gl.GenBuffer()
	mesh.Vertbuffer.Bind(gl.ARRAY_BUFFER)
	gl.BufferData(gl.ARRAY_BUFFER, len(mesh.Vertices)*4, mesh.Vertices, gl.STATIC_DRAW)

	mesh.UVbuffer = gl.GenBuffer()
	mesh.UVbuffer.Bind(gl.ARRAY_BUFFER)
	gl.BufferData(gl.ARRAY_BUFFER, len(mesh.UV)*4, mesh.UV, gl.STATIC_DRAW)

	mesh.Colorbuffer = gl.GenBuffer()
	mesh.Colorbuffer.Bind(gl.ARRAY_BUFFER)
	gl.BufferData(gl.ARRAY_BUFFER, len(mesh.Color)*4, mesh.Color, gl.STATIC_DRAW)

	return mesh, RtBounds
}

func (f *FTFont) GetHalfX(text string) float32 {
	AdvanceX := float32(0)
	for _, textChar := range text {
		if textChar == ' ' {
			AdvanceX += float32(f.FontSize) * 0.5
		}
		if textChar == '\r' || textChar == '\t' {
			continue
		}
		fontChar := f.GetChar(textChar)
		AdvanceX += float32(fontChar.AdvanceX)
	}
	return AdvanceX * 0.5
}

func (f *FTFont) FillFTLabelMesh(text string, SX, SY float64, mesh *Mesh, c4 Color4, bounds *Bounds) {
	AdvanceX := float32(SX)
	AdvanceY := float32(SY)
	Vi := 0
	UVi := 0
	Cli := 0
	ascender := float32(f.faceIndex.GetAscender() / 64)
	for _, textChar := range text {
		if textChar == ' ' {
			AdvanceX += float32(f.FontSize) * 0.5
		}
		if textChar == '\r' || textChar == '\t' {
			continue
		}

		fontChar := f.GetChar(textChar)

		//ch := float32(fontChar.BBox.YMax - fontChar.BBox.YMin)
		XMin := float32(fontChar.BBox.XMin) + AdvanceX
		YMin := float32(fontChar.BBox.YMin) + AdvanceY - ascender
		XMax := float32(fontChar.BBox.XMax) + AdvanceX
		YMax := float32(fontChar.BBox.YMax) + AdvanceY - ascender

		fillTextQuadBy4Point(XMin, YMin, XMax, YMax, Vi, mesh.Vertices)
		Vi += 18
		TMinX := float32(fontChar.TexRect.MinX) / float32(f.MinSize)
		TMaxX := float32(fontChar.TexRect.MaxX) / float32(f.MinSize)
		TMaxY := 1 - (float32(fontChar.TexRect.MinY) / float32(f.MinSize))
		TMinY := 1 - (float32(fontChar.TexRect.MaxY) / float32(f.MinSize))
		fillTextUVBy4Point(TMinX, TMinY, TMaxX, TMaxY, UVi, mesh.UV)
		UVi += 12
		f.fillTextColor(Cli, mesh.Color, c4)
		Cli += 24
		AdvanceX += float32(fontChar.AdvanceX)
	}
	bounds.Min.Y -= float64(ascender)
	bounds.Max.Y -= float64(ascender)
}

func (f *FTFont) fillTextColor(Gi int, colorbuffer []float32, c4 Color4) {
	for j := 0; j < 6; j++ {

		colorbuffer[j*4+Gi] = c4.R
		colorbuffer[j*4+1+Gi] = c4.G
		colorbuffer[j*4+2+Gi] = c4.B
		colorbuffer[j*4+3+Gi] = c4.A

	}
}

func fillTextQuadBy4Point(MinX, MinY, MaxX, MaxY float32, i int, arrayBuffer []float32) {
	arrayBuffer[i] = MinX
	arrayBuffer[i+1] = MaxY
	arrayBuffer[i+2] = 0
	//v1
	arrayBuffer[i+3] = MaxX
	arrayBuffer[i+4] = MinY
	arrayBuffer[i+5] = 0
	//v2
	arrayBuffer[i+6] = MinX
	arrayBuffer[i+7] = MinY
	arrayBuffer[i+8] = 0

	//v3
	arrayBuffer[i+9] = MinX
	arrayBuffer[i+10] = MaxY
	arrayBuffer[i+11] = 0
	//v4
	arrayBuffer[i+12] = MaxX
	arrayBuffer[i+13] = MaxY
	arrayBuffer[i+14] = 0
	//v5
	arrayBuffer[i+15] = MaxX
	arrayBuffer[i+16] = MinY
	arrayBuffer[i+17] = 0
}

func fillTextUVBy4Point(TMinX, TMinY, TMaxX, TMaxY float32, j int, TexBuffer []float32) {
	TexBuffer[j] = TMinX
	TexBuffer[j+1] = TMaxY
	//tv1
	TexBuffer[j+2] = TMaxX
	TexBuffer[j+3] = TMinY
	//tv2
	TexBuffer[j+4] = TMinX
	TexBuffer[j+5] = TMinY
	//tv3
	TexBuffer[j+6] = TMinX
	TexBuffer[j+7] = TMaxY
	//tv4
	TexBuffer[j+8] = TMaxX
	TexBuffer[j+9] = TMaxY
	//tv5
	TexBuffer[j+10] = TMaxX
	TexBuffer[j+11] = TMinY
}
